/*
 * TiffCompressionPackbits
 * 
 * Copyright (c) 2002, 2003 Marco Schmidt.
 * All rights reserved.
 * 
 * C# translation and update 
 * Copyright (c) Adam Sida, asida@centrum.cz
 * 
 * All rights reserved.
 */
using System;
using System.Collections.Generic;
//using System.Drawing;
//using System.Drawing.Imaging;
using System.Windows;


namespace Petra.Gis.ImagingServices.Tiff
{
  /**
   * A TIFF decoder for files compressed with the <em>Packbits</em> method.
   * This compression algorithm has the value <code>32773</code> 
   * in the compression tag of an image file directory.
   * @author Marco Schmidt
   * @since 0.9.0
   */
  public class TiffCompressionPackbits : Compression
  {


    public TiffCompressionPackbits()
      : base() { }



    //public TiffCompressionPackbits(TiffBitmapPage currImage)
    //  : base(currImage){ }



    ////public override Compression CreateCompression(TIFFCodec tifcodec, IPixelImage currImage, int currentTile)
    //public static override Compression CreateCompressionInstance(TiffBitmapPage currImage)
    //{
    //  return new TiffCompressionPackbits(currImage);
    
    //}



    public override byte[] DecompressBlock(BitmapBlock block, byte[] comprData, int srcBitsPerSample)
    {
      //if (_image == null)
      //  throw new InvalidOperationException("no image presents to decompress");


      return decodePackbits(block.Bounds, comprData, srcBitsPerSample);

    }



    //public override void decompress()
    //{

    //  byte[] inputData;  //disk data buffer

    //  //todo: change this all by image type !!!
    //  _blockPixelFormat = PixelFormat.Format8bppIndexed;
    //  _outPixelFormat = _blockPixelFormat;
    //  _bitsPerPixel = 8;
    //  //end

    //  int bandDataBytesLength;

    //  //create new bitmap
    //  Bitmap bmp = new Bitmap(_image.PixelWidth, _image.PixelHeight, _outPixelFormat); //just for now

    //  foreach (KeyValuePair<BandType,Band> bdnKey in _image.Bands)
    //  {
    //    //compute skip bytes for current band, if interleaved
    //    int skipOtherBandBytesLenght = _bitsPerPixel - bdnKey.Value.BitsPerBand;
    //    if ((skipOtherBandBytesLenght % 8) != 0 || (bdnKey.Value.BitsPerBand % 8) != 0)
    //      throw new ArgumentException("unsupported bit depth in image or in band, both must be modulo of 8");

    //    bandDataBytesLength = bdnKey.Value.BitsPerBand >> 3; // = / 8

    //    foreach (Block b in bdnKey.Value.Blocks)
    //    {     
    //      //read compressed data from tiff file
    //      _tiffReader.BaseStream.Seek(b.Offset, System.IO.SeekOrigin.Begin);
    //      inputData = _tiffReader.ReadBytes(b.CompressedSize);

    //     //handle different block dimensions in tiled images
    //      //LockBits by tile size
    //      BitmapData bd = bmp.LockBits(new Rectangle(b.BlockX1, b.BlockY1, b.Width, b.Height), ImageLockMode.ReadWrite, _outPixelFormat);

    //      //get bitmap start pointer
    //      byte* browPtr = (byte*)bd.Scan0;

    //      //for interleaved images
    //      //start to fill image with data from current band first position
    //      browPtr += (int)bdnKey.Key;

    //      //decode directly to bitmap
    //      decodePackbits(inputData, browPtr, bandDataBytesLength, skipOtherBandBytesLenght, bd.Stride);

    //    }
    //  }



    //}


    //public void decode()
    //{
    //  //DataInput din = getInput();
    //  byte[] row = new byte[getBytesPerRow()];
    //  for (int y = getY1(); y <= getY2(); y++)
    //  {
    //    int index = 0;
    //    do
    //    {
    //      byte value = _tiffReader.ReadByte();
    //      if (value >= 0)
    //      {
    //        int numSamples = value + 1;
    //        // copy bytes literally
    //        row = _tiffReader.ReadBytes(numSamples);
    //        index += numSamples;
    //      }
    //      else
    //        //if (value != unchecked((byte)-128))
    //        if (value != 0x80)
    //        {
    //          int numSamples = -value + 1;
    //          // write run
    //          byte sample = _tiffReader.ReadByte();
    //          while (numSamples-- != 0)
    //          {
    //            row[index++] = sample;
    //          }
    //        }
    //    }
    //    while (index != row.Length);
    //    putBytes(row, 0, row.Length);
    //  }
    //}


    /// <summary>
    /// Uncompress packbit compressed image data directly into destination bitmap.
    /// uncompressed data starts to be inserted to destBitmap*
    /// </summary>
    /// <param name="compData">array with compressed data</param>
    /// <param name="destBitmap">pointer to destination bitmap</param>
    /// <param name="dstBmpPixelByteSize">number of bytes allocated for one pixel in destination bitmap (interleave)</param>
    /// <param name="interleaveByteDistance">offset to next value to insert into destination bitmap from decompressed data</param>
    /// <param name="strideByteSize">size of stride of destination bitmap</param>
    private byte[] decodePackbits(Int32Rect bounds, byte[] compData, int dstBmpPixelByteSize)
    {

      if (compData == null || dstBmpPixelByteSize < 1)
        return null;

      int srcCount = 0;
      int dstIndex = 0;
      int counter = 0;
      byte repeat;
      sbyte b;
      //int srcIndex = 0;


      int stride = BitmapBlock.GetStrideLenght(bounds.Width, dstBmpPixelByteSize);
      int outDataLen = stride * bounds.Height;

      if (_uncompressedDataBuff == null)
        _uncompressedDataBuff = new byte[outDataLen];


      if (_uncompressedDataBuff.Length != outDataLen)
        Array.Resize<byte>(ref _uncompressedDataBuff, outDataLen);


      //byte[] destBitmap = new byte[stride * bounds.Height];

      int rowBytesLenght = (bounds.Width * dstBmpPixelByteSize);
      int rowReminder = stride - rowBytesLenght;


      try
      {
        //while (dstCount < arraySize)
        while (srcCount < compData.Length)
        {
          b = (sbyte)compData[srcCount++];

          if (b >= 0 && b <= 127)
          //if (b >= 0x00 && b <= 0x7f)
          {
            // literal run packet
            //for (int i = 0; i < (b + 1); i++)
            //{
            //  destBitmap[dstIndex] = compData[srcCount++];
            //}
            Array.Copy(compData, srcCount, _uncompressedDataBuff, dstIndex, (b + 1));
            srcCount += (b + 1);
            dstIndex += (b + 1);
            counter += (b + 1);
          }
          else if (b <= -1 && b >= -127)
          //else if (b <= 0xff && b >= 0x81)
          {
            // 2 byte encoded run packet
            repeat = compData[srcCount++];
            for (int i = 0; i < (-b + 1); i++)
            {
              _uncompressedDataBuff[dstIndex++] = repeat;
              counter++;
            }
          }
          else
          {
            // no-op packet. Do nothing
          }
          //align to stride...
          if (counter == rowBytesLenght)
            dstIndex += rowReminder;

        }
      }
      catch (IndexOutOfRangeException Ie)
      {
        throw new ApplicationException("invalid input data");
      }

      return _uncompressedDataBuff;
      
    }




    public override CompressionType[] CompressionTypes
    {
      get { return new CompressionType[] { CompressionType.PACKBITS }; }
    }
  }
}
